package cron

import (
	"fmt"
	"sort"
	"sync"
	"time"
)

// Job 定义任务函数类型
type Job func()

// Schedule 定义调度器接口
type Schedule interface {
	Next(now time.Time, timezone *time.Location) time.Time
}

// Entry 用于记录任务快照信息
type Entry struct {
	Name     string
	NextRun  time.Time
	PrevRun  time.Time
	Schedule Schedule
}

// dailySchedule 每天指定时间执行
type dailySchedule struct {
	hour, minute, second int
}

var jobNameArr []string

func init() {
	jobNameArr = make([]string, 0)
}

func (s dailySchedule) Next(now time.Time, timezone *time.Location) time.Time {
	now = now.In(timezone)
	y, m, d := now.Date()
	next := time.Date(y, m, d, s.hour, s.minute, s.second, 0, timezone)
	if !next.After(now) {
		next = next.Add(24 * time.Hour)
	}
	return next
}

// weeklySchedule 每周某一天指定时间执行
type weeklySchedule struct {
	weekday time.Weekday
	hour    int
	minute  int
	second  int
}

func (s weeklySchedule) Next(now time.Time, timezone *time.Location) time.Time {
	now = now.In(timezone)
	y, m, d := now.Date()
	next := time.Date(y, m, d, s.hour, s.minute, s.second, 0, timezone)

	if !next.After(now) {
		next = next.Add(24 * time.Hour)
	}

	for next.Weekday() != s.weekday {
		next = next.Add(24 * time.Hour)
	}

	return next
}

// monthlySchedule 每月某一天指定时间执行
type monthlySchedule struct {
	day    int
	hour   int
	minute int
	second int
}

func (s monthlySchedule) Next(now time.Time, timezone *time.Location) time.Time {
	now = now.In(timezone)
	y := now.Year()
	m := now.Month()

	next := time.Date(y, m, s.day, s.hour, s.minute, s.second, 0, timezone)
	if !next.After(now) {
		m++
		if m > 12 {
			m = 1
			y++
		}
		next = time.Date(y, m, s.day, s.hour, s.minute, s.second, 0, timezone)
	}
	return next
}

// everySchedule 每固定时间执行
type everySchedule struct {
	duration time.Duration
}

func (s everySchedule) Next(now time.Time, _ *time.Location) time.Time {
	return now.Add(s.duration)
}

// onceSchedule 只执行一次
type onceSchedule struct {
	delay time.Duration
}

func (s onceSchedule) Next(now time.Time, _ *time.Location) time.Time {
	return now.Add(s.delay)
}

// Cron 定时任务调度器
type Cron struct {
	timezone *time.Location // 指定该组的时区

	jobs     map[string]*time.Timer
	done     chan struct{}
	wg       sync.WaitGroup
	mu       sync.Mutex
	started  chan struct{}
	entries  map[string]Entry
	entriesM sync.Mutex
	once     sync.Once
}

// Group 表示一个任务分组，每个分组可以有自己的时区
type Group struct {
	Timezone *time.Location
	cron     *Cron
}

// New 创建一个新的 Cron 调度器（默认使用本地时区）
func New() *Cron {
	loc, _ := time.LoadLocation("Local")
	return &Cron{
		timezone: loc,
		jobs:     make(map[string]*time.Timer),
		done:     make(chan struct{}),
		started:  make(chan struct{}),
		entries:  make(map[string]Entry),
	}
}

// NewGroup 创建一个带有时区的任务组
func NewGroup(location string) *Group {
	loc, err := time.LoadLocation(location)
	if err != nil {
		return nil
	}
	return &Group{
		Timezone: loc,
		cron: &Cron{
			timezone: loc,
			jobs:     make(map[string]*time.Timer),
			done:     make(chan struct{}),
			started:  make(chan struct{}),
			entries:  make(map[string]Entry),
		},
	}
}

// Start 启动调度器
func (c *Cron) Start() {
	if len(jobNameArr) == 0 {
		fmt.Println("没有可执行的任务")
	}
	if ok, name := hasDuplicates(jobNameArr); ok {
		msg := fmt.Sprintf("[%s]任务已存在", name)
		fmt.Println(msg)
	}

	c.once.Do(func() {
		close(c.started)
	})
}
func (g *Group) Start() {
	g.cron.Start()
}

// Run 启动调度器并阻塞当前 goroutine
func (c *Cron) Run() {
	<-c.started
	<-c.done
}

// Stop 停止所有任务并等待所有 goroutine 完成
func (c *Cron) Stop() {
	close(c.done)

	c.mu.Lock()
	for name, timer := range c.jobs {
		timer.Stop()
		delete(c.jobs, name)
	}
	c.mu.Unlock()

	c.wg.Wait()
}

// RemoveJob 停止并移除某个任务
func (c *Cron) RemoveJob(name string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if timer, exists := c.jobs[name]; exists {
		timer.Stop()
		delete(c.jobs, name)
	}

	c.entriesM.Lock()
	delete(c.entries, name)
	c.entriesM.Unlock()
}

// Entries 获取当前所有任务的快照
func (c *Cron) Entries() []Entry {
	c.entriesM.Lock()
	defer c.entriesM.Unlock()

	var list []Entry
	for _, entry := range c.entries {
		list = append(list, entry)
	}
	return list
}

// startJob 启动一个一次性或周期性任务
func (c *Cron) startJob(name string, job Job, schedule Schedule) {
	jobNameArr = append(jobNameArr, name)
	c.wg.Add(1)

	go func() {
		defer c.wg.Done()

		<-c.started // 等待启动

		var prevRun time.Time
		for {
			now := time.Now()
			nextTime := schedule.Next(now, c.timezone)
			if nextTime.Before(now) {
				nextTime = schedule.Next(now, c.timezone)
			}

			timer := time.NewTimer(time.Until(nextTime))
			c.mu.Lock()
			if old, exists := c.jobs[name]; exists {
				old.Stop()
			}
			c.jobs[name] = timer
			c.mu.Unlock()

			// 更新任务快照
			c.entriesM.Lock()
			c.entries[name] = Entry{
				Name:     name,
				NextRun:  nextTime,
				PrevRun:  prevRun,
				Schedule: schedule,
			}
			c.entriesM.Unlock()

			select {
			case <-timer.C:
				prevRun = time.Now()
				runJob(name, job)
			case <-c.done:
				timer.Stop()
				c.mu.Lock()
				delete(c.jobs, name)
				c.mu.Unlock()
				c.entriesM.Lock()
				delete(c.entries, name)
				c.entriesM.Unlock()
				return
			}
		}
	}()
}

// runJob 执行任务并处理 panic
func runJob(name string, job Job) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("[%s] recovered from panic: %v\n", name, r)
		}
	}()
	job()
}

// Every 返回每固定时间间隔执行的调度器
func Every(duration time.Duration) Schedule {
	return everySchedule{duration: duration}
}

// Once 返回只执行一次的任务调度器
func Once(delay time.Duration) Schedule {
	return onceSchedule{delay: delay}
}

// DailyAt 返回每天指定时间点的调度器
func DailyAt(hour, min, sec int) Schedule {
	return dailySchedule{hour: hour, minute: min, second: sec}
}

// WeeklyOn 返回每周指定星期几和时间的调度器
func WeeklyOn(weekday time.Weekday, hour, min, sec int) Schedule {
	return weeklySchedule{
		weekday: weekday,
		hour:    hour,
		minute:  min,
		second:  sec,
	}
}

// MonthlyOn 返回每月指定日期和时间的调度器
func MonthlyOn(day, hour, min, sec int) Schedule {
	return monthlySchedule{
		day:    day,
		hour:   hour,
		minute: min,
		second: sec,
	}
}

// Schedule 添加一个自定义调度的任务
func (c *Cron) Schedule(name string, job Job, schedule Schedule) {
	c.startJob(name, job, schedule)
}

// EverySecond 每 N 秒执行一次
func (c *Cron) EverySecond(name string, job Job, sec int) {
	if sec <= 0 {
		sec = 1
	}
	c.Schedule(name, job, Every(time.Duration(sec)*time.Second))
}

func (g *Group) EverySecond(name string, job Job, sec int) {
	g.cron.EverySecond(name, job, sec)
}

// EveryMinute 每 N 分钟整点执行一次
func (c *Cron) EveryMinute(name string, job Job, min int) {
	if min <= 0 {
		min = 1
	}
	c.Schedule(name, job, Every(time.Duration(min)*time.Minute))
}
func (g *Group) EveryMinute(name string, job Job, min int) {
	g.cron.EveryMinute(name, job, min)
}

// EveryHour 每 N 小时整点执行一次
func (c *Cron) EveryHour(name string, job Job, hour int) {
	if hour <= 0 {
		hour = 1
	}
	c.Schedule(name, job, Every(time.Duration(hour)*time.Hour))
}
func (g *Group) EveryHour(name string, job Job, hour int) {
	g.cron.EveryHour(name, job, hour)
}

// EveryDay 每天指定时间执行
func (c *Cron) EveryDay(name string, job Job, hour, min, sec int) {
	c.Schedule(name, job, DailyAt(hour, min, sec))
}
func (g *Group) EveryDay(name string, job Job, hour, min, sec int) {
	g.cron.EveryDay(name, job, hour, min, sec)
}

// EveryWeek 每周几的指定时间执行
func (c *Cron) EveryWeek(name string, job Job, weekday string, hour, min, sec int) {
	// 映射中文星期
	weekdays := map[string]time.Weekday{
		"一": time.Monday,
		"二": time.Tuesday,
		"三": time.Wednesday,
		"四": time.Thursday,
		"五": time.Friday,
		"六": time.Saturday,
		"日": time.Sunday,
	}
	day, ok := weekdays[weekday]
	if !ok {
		panic("invalid weekday")
	}

	c.Schedule(name, job, WeeklyOn(day, hour, min, sec))
}
func (g *Group) EveryWeek(name string, job Job, weekday string, hour, min, sec int) {
	g.cron.EveryWeek(name, job, weekday, hour, min, sec)
}

// EveryMonth 每月几号指定时间执行
func (c *Cron) EveryMonth(name string, job Job, day, hour, min, sec int) {
	c.Schedule(name, job, MonthlyOn(day, hour, min, sec))
}
func (g *Group) EveryMonth(name string, job Job, day, hour, min, sec int) {
	g.cron.EveryMonth(name, job, day, hour, min, sec)
}

func hasDuplicates(items []string) (bool, string) {
	sort.Strings(items) // 先对字符串切片进行排序
	for i := 1; i < len(items); i++ {
		if items[i] == items[i-1] { // 检查相邻元素是否相同
			return true, items[i]
		}
	}
	return false, ""
}
